#include "Parent.h"
#include <algorithm>
#include <iterator>

using namespace std;

Parent::Parent()
{
}

Parent::~Parent()
{
}

void Parent::InsertParent(const STMT parent_statement, const STMT child_statement)
{
	/*
	Assertions:
		1. parent_stmt < child_stmt.
		2. child_stmt must not already be associated with a parent previously.
		3. nodetype of parent must either only be while or if.
	*/
	if ((parent_statement.statement_number < child_statement.statement_number) &&
		(this->GetParent(child_statement).node_type == _NULL) &&
		(parent_statement.node_type == WHILE || parent_statement.node_type == IF))
	{
		/*
		 if parent exists, add child into its current set
		 else if does not exist, initialise new set with the child
		 */
		if (IsParent(parent_statement))
		{
			parent_table[parent_statement].insert(child_statement);
		}
		else
		{
			STMT_ARRAY new_stmt_array;
			new_stmt_array.insert(child_statement);
			parent_table.insert(std::pair<STMT, STMT_ARRAY>(parent_statement, new_stmt_array));
		}
		
		child_table.insert(std::pair<STMT, STMT>(child_statement, parent_statement));
	}
}

STMT_ARRAY Parent::GetChild(const STMT parent_statement)
{
	if (parent_statement.statement_number >= 1)
	{
		std::map<STMT, STMT_ARRAY>::const_iterator it = parent_table.find(parent_statement);
		if (it != parent_table.end())
		{
			return it->second;
		}
	}

	STMT_ARRAY stmt_array;
	return stmt_array;
}

STMT Parent::GetParent(const STMT child_statement)
{
	if (child_statement.statement_number >= 2)
	{
		std::map<STMT, STMT>::const_iterator it = child_table.find(child_statement);
		if (it != child_table.end())
		{
			return it->second;
		}
	}
	
	STMT s;
	s.node_type = _NULL;
	s.statement_number = -1;

	return s;
}

STMT_ARRAY Parent::RetrieveTransitiveChild(const STMT parent_statement)
{
	STMT_ARRAY children; // all children

	STMT_ARRAY immediate_children = this->GetChild(parent_statement);

	std::set<STMT>::const_iterator it = immediate_children.begin();
	for(;it != immediate_children.end(); ++it)
	{ // All immediate children to look for their grandchildren
		children.insert(*it);
		STMT_ARRAY more_children = this->RetrieveTransitiveChild(*it);
		if (more_children.size() > 0)
		{
			set_union(children.begin(), children.end(),
				      more_children.begin(), more_children.end(),
				      inserter(children, children.begin()));
		}
	}

	return children;
}

STMT_ARRAY Parent::RetrieveTransitiveParent(const STMT child_statement)
{
	STMT_ARRAY parents;

	STMT immediate_parent = this->GetParent(child_statement);
	while (immediate_parent.node_type == WHILE || immediate_parent.node_type == IF)
	{
		parents.insert(immediate_parent);
		immediate_parent = this->GetParent(immediate_parent);
	}

	return parents;
}

STMT_ARRAY Parent::RetrieveTransitiveParent(const STMT child_statement, const NODETYPE parent_node_type)
{
	STMT_ARRAY parents;

	STMT immediate_parent = this->GetParent(child_statement);
	while (immediate_parent.node_type == WHILE || immediate_parent.node_type == IF)
	{
		if (immediate_parent.node_type == parent_node_type)
		{
			parents.insert(immediate_parent);
		}
		immediate_parent = this->GetParent(immediate_parent);
	}

	return parents;
}

BOOLEAN Parent::IsParent(const STMT statement)
{
	std::map<STMT, STMT_ARRAY>::const_iterator it = parent_table.find(statement);
	if (it != parent_table.end())
	{
		return true;
	}

	return false;
}